package cc.rengu.igas.channel.upqc.core.packswitch;

import cc.rengu.igas.channel.upqc.common.constant.UpqcConfigConstant;
import cc.rengu.igas.channel.upqc.common.constant.UpqcParamConstant;
import cc.rengu.igas.channel.upqc.common.constant.UpqcTreeNodeConstant;
import cc.rengu.igas.channel.upqc.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.upqc.common.util.UpqcSecurityUtil;
//import cc.rengu.igas.smps.core.realize.SmpsChannelPublicDealService;
//import cc.rengu.igas.smps.core.realize.impl.SmpsChannelPublicDealServiceImpl;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.SrcChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.common.entity.SrcChannelInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.HsmService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.HsmServiceImpl;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.AmountUtil;
import cc.rengu.oltp.utility.util.RSAUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import cc.rengu.utility.base.StringUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.*;

/**
 * 银联服务端报文解包前后、组前包后实现类
 * modify by fuyp 2020-8-27
 */
public class UpqcServerPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String publicKey;
        /*根据url服务名转换交易码*/
        String serverUrl = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/@server_url");
        String recvData = new String(bytes);
        rglog.debug("银联请求报文<{}>", recvData);
        if (!recvData.isEmpty()) {
            Map<String, String> recvMap;
            recvMap = UpqcSecurityUtil.parseJsonString(recvData);
            String charset;
            /*处理编码格式*/
            if (recvMap.get("encoding") != null) {
                charset = recvMap.get("encoding");
                rglog.trace("编码格式为:{}", charset);
            } else {
                rglog.trace("默认UTF-8编码格式...");
                charset = "utf-8";
            }
            String[] kvs = recvData.split("&");
            for (String kv : kvs) {
                String[] tmp = kv.split("=");
                if (tmp.length >= 2) {
                    String key = tmp[0];
                    String value = URLDecoder.decode(tmp[1], charset);
                    recvMap.put(key, value);
                }
            }
            /* 交易来源及交易渠道赋值 */
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_SRC_SYS_ID, ChannelEnum.UPQC.getChannelType());
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID, AppParamConstant.DEFAULT_BIZ_CHANNEL_ID);
            /* 根联银联二维码平台交易赋赋值内部交易码 */
            String reqType = recvMap.get(UpqcTreeNodeConstant.txnType);
            if ("0120000903".equals(reqType)) {
                /*订单查询*/
                String qrCode = recvMap.get("qrCode");
                rglog.debug("银联二维码<{}>", qrCode);
                /*处理二维码取收单机构号*/
                String acqCode = qrCode.substring(qrCode.indexOf("qr.95516.com/") + 13, qrCode.indexOf("/inf"));
                rglog.debug("银联二维码收单机构号<{}>", acqCode);
                SysParamService sysParamService = new SysParamServiceImpl();
                SysParam sysParam = sysParamService.getSysParamInfo(AppParamConstant.DEFAULT_INSTID, UpqcConfigConstant.CUPS_QRCODE_TYPE, acqCode);
                if (null != sysParam && !StringUtil.isNullorEmpty(sysParam.getParamValue())) {
                    rglog.debug("收单机构号对应的法人行行号<{}>", sysParam.getParamValue());
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.INST_ID, sysParam.getParamValue());
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, sysParam.getParamValue());
                } else {
                    rglog.error("无效二维码<{}>，请联系管理人员!", qrCode);
                    xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.respCode, RespCodeEnum.INVALID_QR_CODE.getRespCode());
                    xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.respDesc, RespCodeEnum.INVALID_QR_CODE.getRespDesc());
                    return new byte[0];
                }
            } else if ("0460000903".equals(reqType)) {
                /*条码互通-获取用户标识*/
                String userAuthCode = recvMap.get("userAuthCode");
                if (userAuthCode != null && !userAuthCode.isEmpty()) {
                    userAuthCode = new String(StringUtil.Base64ToBinary(userAuthCode));
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.INST_ID, userAuthCode.substring(0, 3));
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, userAuthCode.substring(0, 3));
                } else {
                    rglog.error("无效的userAuthCode，请联系管理人员!", userAuthCode);
                    xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.respCode, RespCodeEnum.UPQC_TRANS_ERROR.getRespCode());
                    xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.respDesc, RespCodeEnum.UPQC_TRANS_ERROR.getRespDesc());
                    return new byte[0];
                }
            } else if ("0160000903".equals(reqType)) {
                rglog.error("0160000903通知类型");
                return UpqcSecurityUtil.covertResultMap2String(recvMap).getBytes();
            } else if ("0190000903".equals(reqType)) {
                rglog.debug("0190000903通知类型");
                String orderNo = recvMap.get("orderNo");
                String instId = orderNo.substring(5, 8);
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.INST_ID, instId);
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, instId);
            } else {
                /*从请求方保留域获取法人行号*/
                String reqReserved = recvMap.get(UpqcTreeNodeConstant.reqReserved);
                if (reqReserved != null) {
                    JSONObject jsonObject = JSON.parseObject(new String(Base64.decodeBase64(reqReserved)));
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.INST_ID, jsonObject.getString("instId"));
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, jsonObject.getString("instId"));
                } else {
                    rglog.debug("上送保留域为空!");
                    return new byte[0];
                }
            }
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.MSG_SRC_ID, ChannelEnum.UPQC.getChannelType());
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.SRC_SYS_ID, ChannelEnum.UPQC.getChannelType());
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TRANS_CHANNEL_ID, "*");
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, UpqcParamConstant.DEFAULT_BIZ_TYPE);
            /* 获取通道配置信息 */
            String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
            SrcChannelInfoMapper srcChannelInfoMapper = new SrcChannelInfoMapperImpl();
            SrcChannelInfo srcChannelInfo = srcChannelInfoMapper.selectSrcChannelInfoByPrimaryKey(instId, UpqcParamConstant.TUNL_ID_UPQC, "*");
            if (null == srcChannelInfo) {
                rglog.error("获取通道<{}>配置信息失败!", UpqcParamConstant.TUNL_ID_UPQC);
                throw new BizException(RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespCode(), RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespDesc());
            }
            xmlTreeUtil.setXmlTreeObjectValue(UpqcTreeNodeConstant.SRC_CHANNEL_INFO, srcChannelInfo);
            /* 根据通道安全计划获取安全计划 */
            SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
            List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, srcChannelInfo.getSecPlanId());
            if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
                xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
                /* 获取敏感信息加密证书 */
                Optional<SecPlanInfo> checkSignSecPlanOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(UpqcParamConstant.VERIFY_CERT_TYPE)).findFirst();
                if (checkSignSecPlanOpt.isPresent()) {
                    CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                    CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(checkSignSecPlanOpt.get().getInstId() + checkSignSecPlanOpt.get().getSecPlanId() + checkSignSecPlanOpt.get().getSecPlanType());
                    if ("0".equals(certInfo.getCertSaveType())) {
                        publicKey = certInfo.getCertValue();
                    } else if ("1".equals(certInfo.getCertSaveType())) {
                        /*加密机验签*/
                        String signature = recvMap.get(UpqcTreeNodeConstant.signature);
                        if (null == signature || signature.isEmpty()) {
                            /* 签名方法或者签名域为空，直接返回失败 */
                            rglog.error("签名验证失败!");
                        }
                        String stringData = covertMap2String(recvMap);
                        String algorithmType = "SM3";//加密机默认国密摘要
                        rglog.debug("使用加密机加密,算法类型:<{}>,", certInfo.getAlgorithmType());
                        if (certInfo.getAlgorithmType() != null) {
                            if (certInfo.getAlgorithmType().contains("SHA256")) {
                                algorithmType = "SHA256";
                            } else if (certInfo.getAlgorithmType().contains("RSA")) {
                                algorithmType = "SHA1";
                            }
                        }
                        HsmService hsmService = new HsmServiceImpl();
                        if (!hsmService.hsmVerifySignString(null, certInfo.getCertId(), stringData, algorithmType, signature)) {
                            rglog.error("签名验证失败!");
                        }
                        /*验完签名后对qrCode进行urlEncode*/
                        if (recvMap.get("qrCode") != null) {
                            recvMap.put("qrCode", URLEncoder.encode(recvMap.get("qrCode"), charset));
                        }
                        return UpqcSecurityUtil.covertResultMap2String(recvMap).getBytes();
                    } else if ("2".equals(certInfo.getCertSaveType())) {
                        RSAPublicKey rsaPublicKey = RSAUtil.getPublicKeyFromCertFile(certInfo.getCertSavePath());
                        publicKey = Base64.encodeBase64String(Objects.requireNonNull(rsaPublicKey).getEncoded());
                    } else {
                        return bytes;
                    }
                    /* 验证签名 */
                    if (UpqcSecurityUtil.verifySign(UpqcSecurityUtil.covertResultMap2String(recvMap), publicKey)) {
                        rglog.info("签名验证成功!");
                        /*验完签名后对qrCode进行urlEncode*/
                    } else {
                        rglog.error("签名验证失败!");
                        /*验完签名后对qrCode进行urlEncode*/
                    }
                    if (recvMap.get("qrCode") != null) {
                        recvMap.put("qrCode", URLEncoder.encode(recvMap.get("qrCode"), charset));
                    }
                    return UpqcSecurityUtil.covertResultMap2String(recvMap).getBytes();
                }
            }
        }
        return bytes;
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String txnType = xmlTreeUtil.getXmlTreeStringValue("reqType");
        switch (txnType) {
            case "0530000903"://收款通知
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, UpqcParamConstant.PAY_RESULT_NOTICE_TXN);
                break;
            case "0120000903"://订单查询
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, UpqcParamConstant.QRCODE_QUERY_TXN);
                break;
            case "0230000903"://附加处理
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, UpqcParamConstant.ADD_DEAL_TXN);
                break;
            case "0250000903"://c2b交易通知
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, UpqcParamConstant.C2B_RESULT_NOTICE_TXN);
                break;
            case "0460000903"://银联主扫JS 获取用户标识
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, UpqcParamConstant.QRCODE_CHECK_USER_AUTH_CODE);
                break;
            default:
                rglog.info("未知交易类型[{}]", txnType);
                return -1;
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
        if (xmlTreeUtil.isExist(TreeNodeConstant.ORDER_AMT)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.ORDER_AMT, AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.ORDER_AMT)));
        }
        if (xmlTreeUtil.isExist(UpqcTreeNodeConstant.origTxnAmt)) {
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.origTxnAmt, AmountUtil.changeF2Y(xmlTreeUtil.getXmlTreeStringValue(UpqcTreeNodeConstant.origTxnAmt)));
        }
        if (xmlTreeUtil.isExist(UpqcTreeNodeConstant.settleDate)) {
            String settleDate = xmlTreeUtil.getXmlTreeStringValue(UpqcTreeNodeConstant.settleDate);
            xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.settleDate, UpqcSecurityUtil.getCupsSettleDate(settleDate));
        }
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 获取敏感信息加密证书 */
        String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(UpqcTreeNodeConstant.SEC_PLAN_INFO_LIST);
        List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
        Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(UpqcParamConstant.SIGN_CERT_TYPE)).findFirst();
        if (secPlanInfoOpt.isPresent()) {
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            if ("0".equals(certInfo.getCertSaveType())) {
                xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.certId, certInfo.getCertId());
            } else if ("2".equals(certInfo.getCertSaveType())) {
                xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.certId, UpqcSecurityUtil.getCertIdFromCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword()));
            }
            xmlTreeUtil.setXmlTreeObjectValue("certInfo", certInfo);
        }
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
        switch (txnNum) {
            case "P01CH002"://收款通知
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "0530000903");
                break;
            case "P01CH001"://订单查询
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "0120000903");
                break;
            case "T00CH001"://附加处理
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "0230000903");
                break;
            case "P01CH003"://c2b交易通知
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "0250000903");
                break;
            case "T00CH004"://银联主扫JS 获取用户标识
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "0460000903");
                break;
            default:
                rglog.debug("未知交易码类型[{}]", txnNum);
                return -1;
        }
        rglog.debug("返回相应码<{}>,相应信息<{}>", xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_CODE),
                URLDecoder.decode(xmlTreeUtil.getXmlTreeStringValue(UpqcTreeNodeConstant.respDesc), "UTF-8"));
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String content = new String(bytes);
        rglog.debug("未签名报文：{}", content);
        if (!content.isEmpty()) {
            /* 获取签名证书 */
            CertInfo certInfo = (CertInfo) xmlTreeUtil.getXmlTreeObjectValue("certInfo", CertInfo.class);
            /* 加签 */
            String signData = null;
            if ("0".equals(certInfo.getCertSaveType())) {
                signData = UpqcSecurityUtil.noticeSign(content, certInfo.getCertValue());
            } else if ("2".equals(certInfo.getCertSaveType())) {
                List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
                PrivateKey privateKey = RSAUtil.getPrivateKeyFromPfxCertFile(certInfo.getCertSavePath(), aliasList.get(0), certInfo.getCertPassword());
                xmlTreeUtil.setXmlTreeStringValue(UpqcTreeNodeConstant.certId, UpqcSecurityUtil.getCertIdFromCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword()));
                signData = UpqcSecurityUtil.noticeSign(content, privateKey);
            }
            return Objects.requireNonNull(signData).getBytes();

        }
        return bytes;
    }

    /*
     * 银联字符转换
     * */
    private String covertMap2String(Map<String, String> data) {
        TreeMap<String, String> tree = new TreeMap<>();
        Iterator<Map.Entry<String, String>> it = data.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> en = it.next();
            if (UpqcTreeNodeConstant.signature.equals(en.getKey().trim())) {
                continue;
            }
            tree.put(en.getKey(), en.getValue());
        }
        it = tree.entrySet().iterator();
        StringBuilder sf = new StringBuilder();
        while (it.hasNext()) {
            Map.Entry<String, String> en = it.next();
            sf.append(en.getKey()).append(UpqcParamConstant.EQUAL).append(en.getValue()).append(UpqcParamConstant.AMPERSAND);
        }
        return sf.substring(0, sf.length() - 1);
    }
}
